using System;
using System.Collections;
using System.Collections.Generic;
using ValidationFramework.Configuration;
using ValidationFramework.Extensions;
using ValidationFramework.Reflection;

namespace ValidationFramework.Specifications
{
    /// <summary>
    /// Performs duplicate validation check on <see cref="IEnumerable"/>s.
    /// </summary>
    /// <remarks>If the value being validated is null the rule will evaluate to true.</remarks>
    /// <seealso cref="EnumerableDuplicateRuleConfigReader"/>
	/// <seealso cref="EnumerableDuplicateRuleAttribute"/>
#if (!SILVERLIGHT)
    [Serializable]
#endif
    public sealed class ContainsNoDuplicatesSpec
		: SpecificationBase
    {
        // --- Fields

        private static readonly RuntimeTypeHandle handle = typeof (IEnumerable).TypeHandle;

        
        // --- Constructor

        /// <summary>
        /// Initializes a new, empty instance of the <see cref="EnumerableDuplicateRule"/> class using the specified <see cref="IEqualityComparer"/> object. 
        /// </summary>
        public ContainsNoDuplicatesSpec():
			this(null)
        {
        }

        /// <summary>
        /// Initializes a new, empty instance of the <see cref="EnumerableDuplicateRule"/> class using the specified <see cref="IEqualityComparer"/> object. 
        /// </summary>
        /// <param name="errorMessage">The error message for this rule. Pass a null to use the default value.</param>
        /// <param name="useErrorMessageProvider"><c>true</c> to use <see cref="ConfigurationService.ErrorMessageProvider"/> when determining the error message for this <see cref="Rule"/>; otherwise <c>false</c>.</param>
        /// <param name="comparer">The <see cref="IEqualityComparer"/> object that defines how to compare objects for equality.
        /// -or- 
        /// a null reference to use the default hash code provider and the default comparer. The default comparer is each item's implementation of Object.Equals. 
        /// </param>
        /// <param name="ruleSet">A <see cref="string"/> used to group <see cref="Rule"/>s. Use a null to indicate no grouping.</param>
        /// <exception cref="ArgumentException"><paramref name="ruleSet"/> is a <see cref="string.Empty"/>.</exception>
        /// <exception cref="ArgumentException"><paramref name="errorMessage"/> is a <see cref="string.Empty"/>.</exception>
        public ContainsNoDuplicatesSpec(IEqualityComparer comparer)
        {
            Comparer = comparer;
        }

        
        // --- Properties

        /// <summary>
        /// Gets the <see cref="IEqualityComparer"/> object that defines how to compare objects for equality.
        /// </summary>
        public IEqualityComparer Comparer
        {
			get;
			private set;
        }

        
        // --- Methods

		/// <inheritdoc />
		public override string ToString()
		{
			return string.Format("The collection must not contain duplicates.");
		}

		/// <inheritdoc />
		public override bool IsSatisfiedBy(object target)
		{
			if (target == null)
				return true;
		   
			var enumerable = (IEnumerable)target;
	        var list = new List<object>();
	        if (Comparer == null)
	        {
	            foreach (var enumerableValue in enumerable)
	            {
	                foreach (var listValue in list)
	                {
	                    if (Equals(enumerableValue, listValue))
	                    {
	                        return false;
	                    }
	                }
	                list.Add(enumerableValue);
	            }
	        }
	        else
	        {
	            foreach (var enumerableValue in enumerable)
	            {
	                foreach (var listValue in list)
	                {
	                    if (Comparer.Equals(enumerableValue, listValue))
	                    {
	                        return false;
	                    }
	                }
	                list.Add(enumerableValue);
	            }
	        }
			return true;
		}

		/// <inheritdoc />
		public override string DefaultMessage(string tokenizedMemberName, string descriptorType)
		{
			return string.Format("The {0} '{1}' may not contain duplicates.",descriptorType, tokenizedMemberName);
		}

		/// <inheritdoc />
		public override bool CanBeSatisfiedBy(Type type)
		{
			var isGenericIEnumerable = typeof(IEnumerable<>).IsAssignableFrom(type, true);
			var isIEnumerable = typeof(IEnumerable).IsAssignableFrom(type, true);

			return isIEnumerable || isGenericIEnumerable;

			return type.IsAssignableFrom(typeof(IEnumerable));
		}

		/// <inheritdoc />
		public override bool IsEquivalentTo(ISpecification spec)
		{
			var edSpecification = (ContainsNoDuplicatesSpec)spec;
			return edSpecification.Comparer == Comparer;
		}        
    }
}